// --------------------------------------------------------------------------------------------------------------------
// <copyright file="StrategyExpoCell.cs" company="Open Trader">
//   Copyright (c) David Denis (david.denis@systemathics.com)
// </copyright>
// <summary>
//   |  Open Trader - The Open Source Systematic Trading Platform
//   |
//   |  This program is free software: you can redistribute it and/or modify
//   |  it under the terms of the GNU General Public License as published by
//   |  the Free Software Foundation, either version 2 of the License, or
//   |  (at your option) any later version.
//   |
//   |  This program is distributed in the hope that it will be useful,
//   |  but WITHOUT ANY WARRANTY; without even the implied warranty of
//   |  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   |  GNU General Public License for more details.
//   |
//   |  You should have received a copy of the GNU General Public License
//   |  along with this program.  If not, see http://www.gnu.org/licenses
//   |
//   |  Up to date informations about Open Trader can be found at :
//   |    http://opentrader.org
//   |    http://opentrader.codeplex.com
//   |
//   |  For professional services, please visit us at :
//   |    http://www.systemathics.com
// </summary>
// --------------------------------------------------------------------------------------------------------------------

using System;

namespace Org.OpenTrader.Framework.Forge
{
    #region Using Directives

    using System.Collections.Generic;

    using Org.OpenTrader.Framework.Enums;
    using Org.OpenTrader.Framework.Forge.Enums;
    using Org.OpenTrader.Framework.Forge.Interfaces;

    #endregion

    /// <summary>
    /// The strategy expo cell.
    /// </summary>
    public abstract class StrategyExpoCell : IStrategyExpoCell
    {
        #region Constants and Fields

        /// <summary>
        /// Collection of InstrumentExpoCells
        /// </summary>
        protected readonly IList<IInstrumentExpoCell> InstrumentExpoCells = new List<IInstrumentExpoCell>();

        /// <summary>
        /// The identity.
        /// </summary>
        private static readonly Identity Identity = Identity.Create(System.Reflection.MethodBase.GetCurrentMethod());

        /// <summary>
        /// The fees.
        /// </summary>
        private double fees;

        /// <summary>
        /// Long side nominal
        /// </summary>
        private double nominalLong;

        /// <summary>
        /// Short side nominal
        /// </summary>
        private double nominalShort;

        #endregion

        #region Properties

        /// <summary>
        /// Type of the expo
        /// </summary>
        public abstract EExpoCellType ExpoCellType { get; }

        /// <summary>
        /// Check if the values are changed
        /// </summary>
        public bool IsChanged { get; protected set; }

        /// <summary>
        /// Check if updated
        /// </summary>
        public bool IsUpdated { get; protected set; }

        /// <summary>
        /// Check if is updating values
        /// </summary>
        public bool IsUpdating { get; protected set; }

        /// <summary>
        /// LastUpdate Time
        /// </summary>
        public DateTime LastUpdate { get; protected set; }
        
        /// <summary>
        /// Gets Fees.
        /// </summary>
        public double Fees
        {
            get
            {
                this.Reset();
                return this.fees;
            }

            private set
            {
                if (value >= 0.0)
                {
                    this.fees = value;
                }
            }
        }

        /// <summary>
        /// The order event delegate.
        /// </summary>
        public abstract InventoryEventDelegate InventoryEventDelegate { get; }

        /// <summary>
        /// TRUE if NominalLong > NominalShort
        /// </summary>
        public bool IsLong
        {
            get
            {
                return this.NominalLong > this.NominalShort;
            }
        }

        /// <summary>
        /// TRUE if NominalShort > NominalLong  
        /// </summary>
        public bool IsShort
        {
            get
            {
                return this.NominalLong < this.NominalShort;
            }
        }

        /// <summary>
        /// TRUE if NominalExpo not null 
        /// </summary>
        public bool IsOpen
        {
            get
            {
                return this.NominalExpo != 0.0;
            }
        }

        /// <summary>
        /// TRUE if NominalExpo is null 
        /// </summary>
        public bool IsClosed
        {
            get
            {
                return this.NominalExpo == 0.0;
            }
        }

        /// <summary>
        /// Gets NetPL.
        /// </summary>
        public double NetPL
        {
            get
            {
                return this.PL - this.Fees;
            }
        }

        /// <summary>
        /// Return the market expo : Long - Short
        /// </summary>
        public double NominalExpo
        {
            get
            {
                return this.NominalLong - this.NominalShort;
            }
        }

        /// <summary>
        /// Gets NominalLong.
        /// </summary>
        public double NominalLong
        {
            get
            {
                this.Reset();
                return this.nominalLong;
            }

            protected set
            {
                if (value >= 0.0)
                {
                    this.nominalLong = value;
                }
            }
        }

        /// <summary>
        /// Gets NominalShort.
        /// </summary>
        public double NominalShort
        {
            get
            {
                this.Reset();
                return this.nominalShort;
            }

            protected set
            {
                if (value >= 0.0)
                {
                    this.nominalShort = value;
                }
            }
        }

        /// <summary>
        /// The order event delegate.
        /// </summary>
        public abstract OrderEventDelegate OrderEventDelegate { get; }

        /// <summary>
        /// Gets PL.
        /// </summary>
        public double PL
        {
            get
            {
                return this.Realized + this.UnRealized;
            }
        }

        /// <summary>
        /// Gets Realized.
        /// </summary>
        public double Realized { get; private set; }

        /// <summary>
        /// The ref price.
        /// </summary>
        public abstract double RefPrice { get; }

        /// <summary>
        /// Gets UnRealized.
        /// </summary>
        public double UnRealized { get; private set; }

        #endregion

        #region Indexers

        /// <summary>
        /// Get InstrumentExpoCell using index
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public IInstrumentExpoCell this[int index]
        {
            get
            {
                return this.InstrumentExpoCells[index];
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Subscribe the instrument i to this strategy expo cell
        /// </summary>
        /// <param name="i">
        /// instrument i : not null
        /// </param>
        /// <returns>
        /// Return true if can add this instrument
        /// </returns>
        public virtual bool Add(IInstrument i)
        {
            if (i == null)
            {
                Logging.Log(Identity, ELogLevel.Error, () => string.Format("Add : Cannot add Instrument, Invalid Instrument"));
                return false;
            }

            if (i.InstrumentStaticData == null)
            {
                Logging.Log(Identity, ELogLevel.Error, () => string.Format("Add : Cannot add Instrument, Invalid Instrument Static Data"));
                return false;
            }

            if (i.Expo == null)
            {
                Logging.Log(
                    Identity, 
                    ELogLevel.Error, 
                    () =>
                    string.Format(
                        "Add : Cannot add Instrument, Invalid Expo {0} ({1}), Expo type = {2}", 
                        i.InstrumentStaticData.Code, 
                        i.InstrumentStaticData.TradingScope, 
                        this.ExpoCellType));
                return false;
            }

            if (i.Expo[this.ExpoCellType] == null)
            {
                Logging.Log(
                    Identity, 
                    ELogLevel.Error, 
                    () =>
                    string.Format(
                        "Add : Cannot add Instrument, Invalid Instrument Expo Cell {0} ({1}), Instrument Expo Cell Type = {2}", 
                        i.InstrumentStaticData.Code, 
                        i.InstrumentStaticData.TradingScope, 
                        this.ExpoCellType));
                return false;
            }

            Logging.Log(
                Identity, 
                ELogLevel.Debug, 
                () =>
                string.Format(
                    "Add : Adding Instrument {0} ({1}), Instrument Expo Cell Type = {2}", 
                    i.InstrumentStaticData.Code, 
                    i.InstrumentStaticData.TradingScope, 
                    this.ExpoCellType));

            this.InstrumentExpoCells.Add(i.Expo[this.ExpoCellType]);
            return true;
        }

        /// <summary>
        /// Set members to default value (null)
        /// </summary>
        public void Init()
        {
            this.NominalLong = 0.0;
            this.NominalShort = 0.0;
            this.Fees = 0.0;
        }

        /// <summary>
        /// Remove Instrument from expo
        /// </summary>
        /// <param name="i">
        /// </param>
        /// <returns>
        /// The remove.
        /// </returns>
        public virtual bool Remove(IInstrument i)
        {
            if (i == null)
            {
                Logging.Log(Identity, ELogLevel.Error, () => string.Format("Remove : Cannot remove Instrument, Invalid Instrument"));
                return false;
            }

            if (i.InstrumentStaticData == null)
            {
                Logging.Log(Identity, ELogLevel.Error, () => string.Format("Remove : Cannot remove Instrument, Invalid Instrument Static Data"));
                return false;
            }

            if (i.Expo == null)
            {
                Logging.Log(
                    Identity, 
                    ELogLevel.Error, 
                    () =>
                    string.Format(
                        "Remove : Cannot remove Instrument, Invalid Expo {0} ({1}), Expo type = {2}", 
                        i.InstrumentStaticData.Code, 
                        i.InstrumentStaticData.TradingScope, 
                        this.ExpoCellType));
                return false;
            }

            if (i.Expo[this.ExpoCellType] == null)
            {
                Logging.Log(
                    Identity, 
                    ELogLevel.Error, 
                    () =>
                    string.Format(
                        "Remove : Cannot remove Instrument, Invalid Instrument Expo Cell {0} ({1}), Instrument Expo Cell Type = {2}", 
                        i.InstrumentStaticData.Code, 
                        i.InstrumentStaticData.TradingScope, 
                        this.ExpoCellType));
                return false;
            }

            Logging.Log(
                Identity, 
                ELogLevel.Debug, 
                () =>
                string.Format(
                    "Remove : removing Instrument {0} ({1}), Instrument Expo Cell Type = {2}", 
                    i.InstrumentStaticData.Code, 
                    i.InstrumentStaticData.TradingScope, 
                    this.ExpoCellType));

            this.InstrumentExpoCells.Remove(i.Expo[this.ExpoCellType]);
            return true;
        }

        /// <summary>
        /// The reset.
        /// </summary>
        private void Reset()
        {
            if (this.IsUpdating)
            {
                return;
            }

            double l = 0.0;
            double s = 0.0;
            double f = 0.0;

            // Start Updating
            this.IsUpdating = true;

            foreach (var cell in this.InstrumentExpoCells)
            {
                if (cell != null)
                {
                    if (cell.ExpoType == this.ExpoCellType)
                    {
                        l += cell.NominalLong * cell.FactorPrice;
                        s += cell.NominalShort * cell.FactorPrice;
                        f += cell.Fees * cell.FactorPrice;
                    }
                }

                this.NominalLong = l;
                this.NominalShort = s;
                this.Fees = f;
            }

            // End Updating
            this.IsUpdating = false;
        }

        #endregion       
    }
}